package com.ut.animator;

import com.ut.richpath.RichPath;
import com.ut.richpath.pathparser.PathDataNode;
import com.ut.richpath.pathparser.PathParserCompat;
import com.ut.richpath.util.LogUtil;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;

import java.util.ArrayList;
import java.util.List;

public class AnimationBuilder {

    private final long DEFAULT_DURATION = 300L;
    private final long DEFAULT_START_DELAY = 0L;

    private RichPathAnimator richPathAnimator;
    private List<RichPath> paths = new ArrayList<>();

    private List<AnimatorValue> animators = new ArrayList<>();

    private long duration = DEFAULT_DURATION;
    private long startDelay = DEFAULT_START_DELAY;
    private int curveType = Animator.CurveType.CYCLE;
    private RepeatMode repeatMode = RepeatMode.Restart;
    private int repeatCount = 1;


    public AnimationBuilder(RichPathAnimator richPathAnimator, RichPath path) {
        this.richPathAnimator = richPathAnimator;
        this.paths.clear();
        this.paths.add(path);
    }

    public AnimationBuilder(RichPathAnimator richPathAnimator, List<RichPath> paths) {
        this.richPathAnimator = richPathAnimator;
        this.paths.clear();
        this.paths.addAll(paths);
    }

    private void property(String propertyName, float... values) {
        float startValue = values!=null&&values.length>0 ? values[0] : 0;
        float endValue = values!=null&&values.length>1 ? values[1] : 0;
        for (RichPath path : paths) {
            ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(path, propertyName, startValue, endValue);
            applyAnimatorProperties(objectAnimator, path);
        }
    }

    public AnimationBuilder andAnimate(RichPath paths) {
        return richPathAnimator.addAnimationBuilder(paths);
    }

    public AnimationBuilder thenAnimate(RichPath paths) {
        return richPathAnimator.thenAnimate(paths);
    }

    public AnimationBuilder thenAnimate(List<RichPath> paths) {
        return richPathAnimator.thenAnimate(paths);
    }

    /**
     * Custom animation builder.
     *
     * @param listener the AnimationUpdateListener
     * @param values   A set of values that the animation will animate between over time.
     */
    public void custom(AnimationUpdateListener listener, float values) {
        for (RichPath path : paths) {
            AnimatorValue animatorValue = new AnimatorValue();
            animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
                @Override
                public void onUpdate(AnimatorValue animatorValue, float v) {
                    if (listener!=null){
                        listener.update(path, v*values);
                        if (path.getOnRichPathUpdatedListener()!=null){
                            path.getOnRichPathUpdatedListener().onPathUpdated();
                        }
                    }
                }
            });
            applyAnimatorProperties(animatorValue, path);
        }
    }

    public RichPathAnimator start() {
        richPathAnimator.start();
        return richPathAnimator;
    }

    public void cancel() {
        richPathAnimator.cancel();
    }

    public AnimationBuilder duration(long duration){
        this.duration = duration;
        for (AnimatorValue animator : animators) {
            animator.setDuration(duration);
        }
        return this;
    }

    public AnimationBuilder curveType(int curveType){
        this.curveType = curveType;
        for (AnimatorValue animator : animators) {
            animator.setCurveType(curveType);
        }
        return this;
    }

    public void durationSet(long duration) {
        richPathAnimator.duration = duration;
    }

    public void startDelay(long startDelay) {
        this.startDelay = startDelay;
        for (AnimatorValue animator : animators) {
            animator.setDelay(startDelay);
        }
    }

    public void startDelaySet(long startDelay) {
        richPathAnimator.startDelay = startDelay;
    }

//    fun interpolator(interpolator: Interpolator) {
//        this.interpolator = interpolator
//        for (animator in animators) {
//            animator.interpolator = interpolator
//        }
//    }

//    fun interpolatorSet(interpolator: Interpolator) {
//        richPathAnimator.interpolator = interpolator
//    }

    public void repeatMode(RepeatMode repeatMode) {
        this.repeatMode = repeatMode;
        for (AnimatorValue animator : animators) {
            animator.setCurveType(repeatMode.getValue());
        }
    }

    public void repeatModeSet(RepeatMode repeatMode) {
        richPathAnimator.repeatMode = repeatMode;
    }

    public void repeatCount(int repeatCount) {
        this.repeatCount = repeatCount;
        for (AnimatorValue animator : animators) {
            animator.setLoopedCount(repeatCount);
        }
    }

    public void repeatCountSet(int repeatCount) {
        richPathAnimator.repeatCount = repeatCount;
    }

    public AnimationBuilder fillColor(int... colors) {
        color("fillColor", colors);
        return this;
    }

    public AnimationBuilder strokeColor(int colors) {
        color("strokeColor", colors);
        return this;
    }

    private AnimationBuilder color(String propertyName, int... colors) {
        int startValue = colors!=null&&colors.length>0 ? colors[0] : 0;
        int endValue = colors!=null&&colors.length>1 ? colors[1] : 0;
        for (RichPath path : paths) {
            ArgbEvaluator argbEvaluator = new ArgbEvaluator();
            ObjectAnimator objectAnimator = ObjectAnimator.ofArgb(path, propertyName, argbEvaluator, startValue, endValue);
            applyAnimatorProperties(objectAnimator, path);
        }
        return this;
    }

    public void pathData(String pathData) {
        List<List<PathDataNode>> pathDataNodes = new ArrayList<>();
        for (int i=0; i< pathData.length(); i++) {
            List<PathDataNode> pathDataNodeList = PathParserCompat.createNodesFromPathData(pathData.charAt(i)+"");
            if (pathDataNodeList!=null){
                pathDataNodes.add(pathDataNodeList);
            }
        }

        if (!PathParserCompat.canMorph(pathDataNodes)) {
            LogUtil.i("RichPathAnimator, the paths aren't compatible for morphing. The paths should have exact same length of commands, and exact same length of parameters for each command");
            return;
        }

        List<PathDataNode> startValue = pathDataNodes.size()>0 ? pathDataNodes.get(0) : null;
        List<PathDataNode> endValue = pathDataNodes.size()>1 ? pathDataNodes.get(1) : null;

        for (RichPath path : paths) {
            PathEvaluator pathEvaluator = new PathEvaluator();
            ObjectAnimator objectAnimator = ObjectAnimator.ofObject(path, "pathDataNodes", pathEvaluator, startValue, endValue);
            applyAnimatorProperties(objectAnimator, path);
        }
    }

    private void applyAnimatorProperties(AnimatorValue animator, RichPath path){
        if (path==null)return;
        animator.setDuration(duration);
        animator.setDelay(startDelay);
        animator.setCurveType(repeatMode.getValue());
        animator.setLoopedCount(repeatCount);
//        interpolator?.let { animator.interpolator = it }
        //add animator to the animators list
        this.animators.add(animator);
    }

    public AnimationBuilder strokeAlpha(float... alpha) {
        property("strokeAlpha", alpha);
        return this;
    }

    public AnimationBuilder fillAlpha(float... alpha) {
        property("fillAlpha", alpha);
        return this;
    }

    public AnimationBuilder size(float width, float height) {
        property("width", width);
        property("height", height);
        return this;
    }

    public AnimationBuilder scaleX(float... values) {
        property("scaleX", values);
        return this;
    }

    public AnimationBuilder scaleY(float... values) {
        property("scaleY", values);
        return this;
    }

    public AnimationBuilder scale(float... values) {
        scaleX(values);
        scaleY(values);
        return this;
    }

    public AnimationBuilder width(float... values) {
        property("width", values);
        return this;
    }

    public AnimationBuilder height(float... values) {
        property("height", values);
        return this;
    }

    public AnimationBuilder rotation(float... values) {
        property("rotation", values);
        return this;
    }

    public AnimationBuilder translationY(float... values) {
        property("translationY", values);
        return this;
    }

    public AnimationBuilder translationX(float... values) {
        property("translationX", values);
        return this;
    }

    public AnimationBuilder trimPathStart(float... values) {
        property("trimPathStart", values);
        return this;
    }

    public AnimationBuilder trimPathEnd(float... values) {
        property("trimPathEnd", values);
        return this;
    }

    public AnimationBuilder trimPathOffset(float values) {
        property("trimPathOffset", values);
        return this;
    }

    public AnimationBuilder animationListener(AnimationListener listener) {
        richPathAnimator.animationListener = listener;
        return this;
    }

    public List<AnimatorValue> getAnimators() {
        return animators;
    }
}
